/**
 * This file describes the class state: its instances
 * are solutions to a clustering problem.
 * The methods of a state are used by the metaheuristics.
 *
 * @author Carlos Colmenares (06-39380)
 * @author Kelwin Fernandez (07-40888)
 */

#ifndef __CLUSTROPHOBIA__STATE_H_
#define __CLUSTROPHOBIA__STATE_H_

#include "clustering_instance.h"
#include <utility>
#include <vector>

using namespace std;

/**
 * This class models a solution to the instance of a
 * problem. The specific problem which solution is
 * hold in tha instance of the class is specified
 * by the clustering_instance variable.
 * An instance of the solution can calculate its value,
 * its neighborhood and other operations needed for
 * the methaheuristics internal functionality.
 */
class state{
    public:
        /**
         * This variable holds the state's quality meassure
         */
        double value;
        /**
         * This array's size is equal to num_clusters
         * and holds the centroid's coordinate values
         */
        double **centroids;
        /**
         * This variable holds the number of clusters
         * in the current solution
         */
        int num_clusters;
        /**
         * This array's size is equal to prob->n_data,
         * which is the number of data samples in the
         * problem's instance. This array holds, for each
         * of such data points, the distance between each point
         * and its closest centroid to the point.
         * Thus, the array has to be recalculated
         * everytime a centroid is moved. The cercanity meassure
         * is always the euclidean distance.
         */
        double *distance_to_closest_centroid;
        /**
         * This array's size is equal to prob->n_data,
         * which is the number of data samples in the
         * problem's instance. This array holds, for each
         * of such data points, the number of the centroid which
         * is the closest to the point.
         * Thus, the array has to be recalculated
         * every time a centroid is moved. The closeness measure
         * is always the euclidean distance.
         */
        int *closest_centroid;
        /**
         * This variable holds the instance of the problem
         * which solution is hold by the class instance
         */
        clustering_instance* prob;
        /**
         * Class constructor
         * @param p The description of the problem that will
         * be threated by the class instance
         * @param nclusters The number of clusters that
         * the solution will have
         */
        state(clustering_instance* p, int nclusters);
        /**
         * Class constructor, it copies the other instance
         * that is passed as an argument.
         * @param other The instance to be copied
         */
        state(state* other);
        //WTF?
        state(clustering_instance* p, vector<int> asign[]);
        /**
         * Class destructor, it frees all the instance's
         * structures
         */
        ~state();
        /**
         * Changes the centroids array and sets new random centroids,
         * which are placed over random data points
         */
        void initial_state();
        /**
         * Calculates the solution (neighbor) equal to approaching
         * the indicated centroid to the indicated point
         * @param cent The number of the centroid that will be moved
         * @param point The point towards which the centroid will be
         * approached to
         */
        state* get_neighbor(int cent, int point);
        /**
         * Calculates the solution (neighbor) equal to moving
         * the indicated centroid to the geometrical center
         * of the cluster induced by it and then approaching
         * it to the indicated point.
         * @param cent The number of the centroid that will be moved
         * @param point The point towards which the centroid will be
         * approached to
         */
        state* get_smooth_neighbor(int cent, int point);
        /**
         * Returns randomly a neighbor in the neighborhood
         * of the passed state
         * @return The neighbor of the solution
         */
        state* get_random_neighbor();
        /**
         * Disturbs the solution by randomly changing the position
         * of some centroids.
         * @return A new solution with some centroids on the
         * same position as the centroids of the instance on
         * which the method was applied.
         */
        state* disturb();
        /**
         * Returns the quality value of the solution
         * @return The quality value of the solution
         */
        double get_value();
        /**
         * The number of the criterion of quality to be
         * used for evaluating the value of the solution.
         * 0 is for Davies-Bouldin criterion
         * 1 is for Dunn criterion
         */
        static int criterion_id;
        /**
         * Compares two states and returns true if the
         * second state is better than the first one. The
         * comparison is made according to the quality
         * index indicated in the criterion_id variable.
         * @param current The first state to compare
         * @param neighbor The second state to compare
         * @return True if the second solution has a higher
         * quality than the first one. False otherwise 
         */
        static bool acceptance_criteria( state* current, 
                state* neighbor);
        /**
         * Prints relevant information about the object
         */
        void print();
        /**
         * Calculates from scratch the criterion of quality,
         * which implies to calculate for each data point its
         * closest centroid
         * @return The calculated value for the criterion
         */
        double calc_criterion();
        /**
         * Recalculates the criterion of quality, but not from
         * scratch, the method supposes that the criterion
         * was previously calculated and then the sole centroid
         * that changed its position was the on passed as an argument
         * @param centroid_moved The centroid that was moved. If
         * another centroid was moved, then the method will not
         * calculate the correct quality value
         * @return The calculated value for the criterion
         */
        double act_criterion(int centroid_moved);
        /**
         * Calculates the closest centroid to each of the
         * data points
         */
        void assign_nearest();
        /**
         * Moves all the centroids to the geometrical
         * center of their induced centroids
         */
        void recompute_centroids();
        /**
         * Erases the centroids which induced clusters
         * does not have any point
         */
        void filter_unused_clusters();
    
    private:
       
        /**
         * Calculates the Davies-Bouldin quality criterion
         * from scratch, which implies calculating for each
         * data point its closest centroid
         * @return The calculated value
         */
        double davies_bouldin_index();
        /**
         * Calculates the Davies-Bouldin quality criterion,
         * but not from scratch, it supposes that the criterion
         * was already calculated and the sole centroid that was
         * moved since then is the indicated as a parameter
         * @param centroid_moved The centroid that was moved since
         * the calculation of the criterion
         * @return The calculated value
         */
        double davies_bouldin_index(int centroid_moved);
        /**
         * Calculates the Dunn quality criterion
         * from scratch, which implies calculating for each
         * data point its closest centroid
         * @return The calculated value
         */
        double dunn_index();
        /**
         * Calculates the Dunn quality criterion,
         * but not from scratch, it supposes that the criterion
         * was already calculated and the sole centroid that was
         * moved since then is the indicated as a parameter
         * @param centroid_moved The centroid that was moved since
         * the calculation of the criterion
         * @return The calculated value
         */
        double dunn_index(int centroid_moved);
        /**
         * Calculates the closest centroid to the
         * data point which number is indicated as
         * a parameter
         * @param p The point which closest centroid is to be
         * calculated
         */
        void calc_closest_centroid(int p);
        /**
         * For each point in the data points, calculates
         * its closest centroid
         */
        void calc_closest_clusters();

        //This variables are used for the dunn index
        double min_inter_distance;
        std::pair<int,int> closest_clusters;

        /**
         * The point in the data whose distance to
         * the closest centroid is the maximum of
         * the dataset.
         */
        double max_intra_distance;
        /**
         * The number of the point in the data points which
         * Distance to its closest centroid is the maximal in
         * The whole set
         */
        int max_intra_d_point;
};

#endif
